package com.paw.hys.sa.controller;



import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import com.paw.hys.sa.client.ServiceHbClient;
import com.paw.hys.sa.content.ContentHolder;
import com.paw.hys.sa.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author lvzihai
 * @date 2022/8/25
 **/
@Slf4j
@RestController
public class ServiceHaController {

  @Autowired
  ServiceHbClient serviceHbClient;

  @GetMapping("/welcomeHa")
  public String welcomeHa () {
    ThreadUtil.sleep(RandomUtil.randomInt(2000));
    log.info("welcomeHa ThreadContent val={}", ContentHolder.getValue());
    return "serviceHa";
  }


  /**
   * @return
   * @HystrixCommand注解的各个参数： 　　1：commandKey：配置全局唯一标识服务的名称，比如，库存系统有一个获取库存服务，那么就可以为这个服务起一个名字来唯一识别该服务，如果不配置，则默认是@HystrixCommand注解修饰的函数的函数名。
   * <p>
   * 　　2：groupKey：一个比较重要的注解，配置全局唯一标识服务分组的名称，比如，库存系统就是一个服务分组。通过设置分组，Hystrix会根据组来组织和统计命令的告、仪表盘等信息。Hystrix命令默认的线程划分也是根据命令组来实现。默认情况下，Hystrix会让相同组名的命令使用同一个线程池，所以我们需要在创建Hystrix命令时为其指定命令组来实现默认的线程池划分。此外，Hystrix还提供了通过设置threadPoolKey来对线程池进行设置。建议最好设置该参数，使用threadPoolKey来控制线程池组。
   * <p>
   * 　　3：threadPoolKey：对线程池进行设定，细粒度的配置，相当于对单个服务的线程池信息进行设置，也可多个服务设置同一个threadPoolKey构成线程组。
   * <p>
   * 　　4：fallbackMethod：@HystrixCommand注解修饰的函数的回调函数，@HystrixCommand修饰的函数必须和这个回调函数定义在同一个类中，因为定义在了同一个类中，所以fackback method可以是public/private均可。
   * <p>
   * 　　5：commandProperties：配置该命令的一些参数，如executionIsolationStrategy配置执行隔离策略，默认是使用线程隔离，此处我们配置为THREAD，即线程池隔离。参见：com.netflix.hystrix.HystrixCommandProperties中各个参数的定义。
   * <p>
   * 　　6：threadPoolProperties：线程池相关参数设置，具体可以设置哪些参数请见：com.netflix.hystrix.HystrixThreadPoolProperties 　　7：ignoreExceptions：调用服务时，除了HystrixBadRequestException之外，其他@HystrixCommand修饰的函数抛出的异常均会被Hystrix认为命令执行失败而触发服务降级的处理逻辑（调用fallbackMethod指定的回调函数），所以当需要在命令执行中抛出不触发降级的异常时来使用它，通过这个参数指定，哪些异常抛出时不触发降级（不去调用fallbackMethod），而是将异常向上抛出。
   * 　　8：observableExecutionMode：定义hystrix observable command的模式； 9：raiseHystrixExceptions：任何不可忽略的异常都包含在HystrixRuntimeException中；
   * 10：defaultFallback：默认的回调函数，该函数的函数体不能有入参，返回值类型与@HystrixCommand修饰的函数体的返回值一致。如果指定了fallbackMethod，则fallbackMethod优先级更高。
   */

  @HystrixCommand(commandKey = "hysTestEx", fallbackMethod = "hysTestExFallBack", ignoreExceptions = {BizException.class})
  @GetMapping("/hysTestEx")
  public String hysTestEx () {
    int randomInt = RandomUtil.randomInt(100);
    if (randomInt > 50) {
      throw new BizException("hystrix test BizException");
    }
    return "serviceHa";
  }


  /**
   * 熔断
   * <p>
   * hystrix.command.[commandkey].circuitBreaker.enabled 是否开启熔断机制，默认为true。
   * <p>
   * 　　hystrix.command.[commandkey].circuitBreaker.forceOpen 强制开启熔断，默认为false。
   * <p>
   * 　　hystrix.command.[commandkey].circuitBreaker.forceClosed 强制关闭熔断，默认为false。
   * <p>
   * 　　hystrix.command.[commandkey].circuitBreaker.sleepWindowInMilliseconds  熔断窗口时间，默认为5s。
   * <p>
   * 　　hystrix.command.[commandkey].circuitBreaker.requestVolumeThreshold 当在配置时间窗口内达到此数量后的失败，进行短路。默认20个
   * <p>
   * 　　hystrix.command.[commandkey].circuitBreaker.errorThresholdPercentage 出错百分比阈值，当达到此阈值后，开始短路。默认50%
   *
   * @return
   */
  @HystrixCommand(commandKey = "hysBreaker", fallbackMethod = "hysBreakerFallBack",
      commandProperties = {
          @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED, value = "true"),
          @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5"),
          @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "2"),//default 20
          @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),//default 50
      }
  )
  @GetMapping("/hysBreaker")
  public String hysBreaker () {
    log.info("ServiceHaController#hysBreaker method =====");
    log.info("hysBreaker ThreadContent val={}", ContentHolder.getValue());
    ThreadUtil.sleep(RandomUtil.randomInt(2000));

    return "hysBreaker success";
  }


  /**
   * 信号量隔离
   *
   * @return
   */
  @HystrixCommand(commandKey = "hysSemaphoreRateLimit", fallbackMethod = "hysSemaphoreRateLimitFallBack",
      commandProperties = {
      @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_TIMEOUT_ENABLED,value = "true"),
          @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS,value = "1000"),
          @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
          @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "10"),
          @HystrixProperty(name = HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "10"),
      }

  )
  @GetMapping("/hysSemaphoreRateLimit")
  public String hysSemaphoreRateLimit () {
    log.info("in method hysSemaphoreRateLimit ====");
    log.info("hysSemaphoreRateLimit ThreadContent val={}", ContentHolder.getValue());
    ThreadUtil.sleep(500);
    return "hysSemaphoreRateLimit success";
  }


//  public static final String MAX_QUEUE_SIZE = "maxQueueSize";
//  public static final String CORE_SIZE = "coreSize";
//  public static final String KEEP_ALIVE_TIME_MINUTES = "keepAliveTimeMinutes";
//  public static final String QUEUE_SIZE_REJECTION_THRESHOLD = "queueSizeRejectionThreshold";
//  public static final String METRICS_ROLLING_STATS_NUM_BUCKETS = "metrics.rollingStats.numBuckets";
//  public static final String METRICS_ROLLING_STATS_TIME_IN_MILLISECONDS = "metrics.rollingStats.timeInMilliseconds";
  /**
   * 线程池隔离
   * 切换线程
   * ## ThreadLocal 上下文变量丢失
   * @return
   */
  @HystrixCommand(commandKey = "hysThreadRateLimit", threadPoolKey = "hysThreadRateLimit", fallbackMethod = "hysThreadRateLimitFallBack",
      threadPoolProperties = {
        @HystrixProperty(name = HystrixPropertiesManager.CORE_SIZE,value = "1"),//10
      }
  )
  @GetMapping("/hysThreadRateLimit")
  public String hysThreadRateLimit () {
    log.info("in method hysThreadRateLimit ====");
    // 验证ThreadContent数据
    log.info("hysThreadRateLimit ThreadContent val={}", ContentHolder.getValue());
    ThreadUtil.sleep(RandomUtil.randomInt(1000));
    return "hysThreadRateLimit success";
  }

  @GetMapping("/callHb")
  public String callHb () {
    String result = serviceHbClient.callHc();

    return "in ha method callHb result: "+result ;
  }

  public String hysTestExFallBack () {
    log.error("hysTestExFallBack error");
    return "hysTestExFallBack";
  }

  public String hysRateLimitFallBack () {
    log.error("hysRateLimitFallBack error");
    return "hysRateLimitFallBack";
  }

  public String hysBreakerFallBack () {
    log.error("hysBreakerFallBack");
    return "hysBreakerFallBack";
  }

  public String hysSemaphoreRateLimitFallBack () {
    log.error("hysSemaphoreRateLimitFallBack error");
    return "hysSemaphoreRateLimitFallBack";
  }

  public String hysThreadRateLimitFallBack () {
    log.error("hysThreadRateLimitFallBack error");
    return "hysThreadRateLimitFallBack";
  }

}
